package com.lyl.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.lyl.model.WebLog;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author lylbell
 * @date 2021-06-21 15:16
 */
@Component  //放入ioc容器中
@Aspect       //定义为一个切面
@Order(1)  //执行顺序
@Slf4j
public class WebLogAspect {

    /**
     * 环绕通知：方法执行之前，执行之后
     */

    /**
     * 定义切入点
     */
    @Pointcut("execution(* com.lyl.controller.*.*(..))")
    public void webLog() {
    }


    @Around(value = "webLog()")
    public Object recodeWebLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = null;
        WebLog webLog = new WebLog();
        Long beginTime = System.currentTimeMillis();

        //执行方法的调用
        result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        long endTime = System.currentTimeMillis();

        //获取当前请求的request对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //获取安全的上下文
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        String url = request.getRequestURL().toString();  //请求的url

        //获取方法。方法上有@ApiOperation,注解里边有描述value
        MethodSignature methodSignature = (MethodSignature)proceedingJoinPoint.getSignature();
        Method method = methodSignature.getMethod();
        //获取方法上的注解@ApiOperation
        ApiOperation annotation = method.getAnnotation(ApiOperation.class);

        //获取类的名称
        String className = proceedingJoinPoint.getTarget().getClass().getName();

        webLog.setSpendTime((int)(endTime-beginTime)/100);  //请求接口花费的时间
        webLog.setUri(request.getRequestURI());
        webLog.setUrl(url);
        webLog.setBasePath(StrUtil.removeSuffix(url, URLUtil.url(url).getPath())); //http://ip:port/
        //log.info("安全的上下文"+JSON.toJSONString(authentication,true));
        //log.info("安全用户名"+authentication.getPrincipal().toString());
        webLog.setUsername(authentication == null ? "anonymous":authentication.getPrincipal().toString());  //获取用户名，存的是id
        webLog.setIp(request.getRemoteAddr());
        webLog.setDescription(annotation == null ? "no desc":annotation.value());
        webLog.setMethod(className + "." + method.getName());
        webLog.setParameter(getMethodParameter(method,proceedingJoinPoint.getArgs()));
        webLog.setResult(result);
        //log.info(JSON.toJSONString(webLog,true));//true：格式化
        return result;

    }

    /**
     * 获取方法上的参数
     * @param method
     * @param args
     * @return
     */
    private Object getMethodParameter(Method method, Object[] args) {
        LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        //方法的形参名字
        String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(method);

        Map<String,Object> methodParameter = new LinkedHashMap<>();
        for (int i=0;i < parameterNames.length;i++) {
            if (parameterNames[i].equals("password") || parameterNames.equals("file")) {  //MultipartFile  使用fastjson 序列化会报错，因此我们需要忽略掉它
                methodParameter.put(parameterNames[i],"受限的支持类型");
            }else {
                methodParameter.put(parameterNames[i],args[i]);
            }
        }
        return methodParameter;
    }


   
}
